import * as signalR from "@microsoft/signalr";
import axios from "axios";
import qs from "qs";
import { HubConnectionState } from "@microsoft/signalr";

const hubUrl = import.meta.env.VITE_DEVICE_MONITOR_SERVICE_BASE_URL + "/monitor-service-hub";
const loginUrl = import.meta.env.VITE_AUTH_SERVICE_BASE_URL + "/connect/token";
const signaturesUrl =
  import.meta.env.VITE_DEVICE_MONITOR_SERVICE_API_BASE_URL + "/signatures/add-claims";

type BearerHttpResponse = {
  access_token: string;
  expires_in: number;
  token_type: string;
  scope: string;
};

class SignalRClient {
  private hubConnection: signalR.HubConnection;

  constructor(groupName: string) {
    const options: signalR.IHttpConnectionOptions = {
      accessTokenFactory: async () => {
        const token = await this.getAccessTokenAsync(groupName);
        return token;
      }
    };

    this.hubConnection = new signalR.HubConnectionBuilder()
      .withUrl(hubUrl, options)
      .withAutomaticReconnect()
      .build();
  }

  // 获取访问令牌
  private async getAccessTokenAsync(groupName: string) {
    const response = await axios.post(
      loginUrl,
      qs.stringify({
        client_id: "MonitorServiceApp",
        client_secret: "1q2w3e*",
        grant_type: "client_credentials"
      })
    );
    const bearerResponse = response.data as BearerHttpResponse;
    const accessToken = await this.signaturesTokenAsync(groupName, bearerResponse);
    return accessToken;
  }

  // 对访问令牌进行签名
  private async signaturesTokenAsync(groupName: string, bearerResponse: BearerHttpResponse) {
    const response = await axios.post(
      signaturesUrl,
      {
        claims: {
          signalr_connection_type: "group",
          signalr_connection_name: groupName
        }
      },
      {
        headers: {
          Authorization: bearerResponse.token_type + " " + bearerResponse.access_token
        }
      }
    );

    return response.data.accessToken as string;
  }

  async connectAsync() {
    if (this.isConnected()) {
      return;
    }

    await this.hubConnection.start();
  }

  getConnection() {
    return this.hubConnection;
  }

  isConnected() {
    return this.hubConnection.state === signalR.HubConnectionState.Connected;
  }

  async disconnectAsync() {
    if (this.hubConnection.state === HubConnectionState.Connected) await this.hubConnection.stop();
  }
}

export { SignalRClient };
